#include "stdafx.h"
#include "IExample.h"
#include "EnumList.h"
#include "RealIEnumVARIANT.h"
#include "RealICollection.h"

ITypeInfo *CollectionTypeInfo;

HRESULT LoadCollectionTypeInfo()
{
    HRESULT hr;
    ITypeLib *pTypeLib = NULL;
    hr = LoadRegTypeLib(TLBID_IExample, 1, 0, 0, &pTypeLib);
    if (FAILED(hr)) return hr;
    hr = pTypeLib->GetTypeInfoOfGuid(IID_ICollection, &CollectionTypeInfo);
    pTypeLib->Release();
    if (FAILED(hr)) return hr;
    CollectionTypeInfo->AddRef();
    return S_OK;
}

RealICollection::RealICollection(EnumList *pList) : m_count(1), m_list(pList)
{
    if (m_list) m_list->AddRef();
}

RealICollection::~RealICollection()
{
    if (m_list) m_list->Release();
}

HRESULT STDMETHODCALLTYPE RealICollection::QueryInterface(REFIID riid, void **ppv)
{
    if (IsEqualIID(riid, IID_IUnknown) ||
        IsEqualIID(riid, IID_IDispatch) ||
        IsEqualIID(riid, IID_ICollection))
    {
        *ppv = this;
        AddRef();
        return S_OK;
    }

    *ppv = NULL;
    return E_NOINTERFACE;
}

ULONG STDMETHODCALLTYPE RealICollection::AddRef()
{
    return ++m_count;
}

ULONG STDMETHODCALLTYPE RealICollection::Release()
{
    if (--m_count == 0)
    {
        delete this;
        InterlockedDecrement(&OutstandingObjects);
        return 0;
    }
    return m_count;
}

HRESULT STDMETHODCALLTYPE RealICollection::GetTypeInfoCount(UINT *pctinfo)
{
    *pctinfo = 1;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE RealICollection::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
    *ppTInfo = NULL;
    if (iTInfo != 0) return DISP_E_BADINDEX;
    if (CollectionTypeInfo != NULL)
    {
        CollectionTypeInfo->AddRef();
    }
    else
    {
        HRESULT hr = LoadCollectionTypeInfo();
        if (FAILED(hr)) return hr;
    }
    *ppTInfo = CollectionTypeInfo;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE RealICollection::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
{
    if (CollectionTypeInfo == NULL)
    {
        HRESULT hr = LoadCollectionTypeInfo();
        if (FAILED(hr)) return hr;
    }
    return DispGetIDsOfNames(CollectionTypeInfo, rgszNames, cNames, rgDispId);
}

HRESULT STDMETHODCALLTYPE RealICollection::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
                                                  WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
                                                  EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    if (!IsEqualIID(riid, IID_NULL)) return DISP_E_UNKNOWNINTERFACE;
    if (CollectionTypeInfo == NULL)
    {
        HRESULT hr = LoadCollectionTypeInfo();
        if (FAILED(hr)) return hr;
    }
    return DispInvoke(this, CollectionTypeInfo, dispIdMember, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
}

HRESULT STDMETHODCALLTYPE RealICollection::Count(LONG *pNum)
{
    if (pNum == NULL) return E_INVALIDARG;

    if (m_list == NULL)
    {
        *pNum = 0;
        return S_OK;
    }

    LONG count = 0;
    EnumItem *item = m_list->head;
    while (item != NULL)
    {
        ++count;
        item = item->next;
    }
    *pNum = count;
    return S_OK;
}

HRESULT STDMETHODCALLTYPE RealICollection::Item(LONG index, VARIANT *pData)
{
    if (pData == NULL) return E_INVALIDARG;
    pData->vt = VT_EMPTY;
    if (m_list == NULL) return S_FALSE;
    EnumItem *item = m_list->head;
    while (item && index--) item = item->next;
    if (item == NULL) return S_FALSE;
    return VariantCopy(pData, &item->data);
}

HRESULT STDMETHODCALLTYPE RealICollection::_NewEnum(IUnknown **pUnk)
{
    if (pUnk == NULL) return E_INVALIDARG;
    RealIEnumVARIANT *p = new RealIEnumVARIANT(m_list);
    if (p == NULL)
    {
        *pUnk = NULL;
        return E_OUTOFMEMORY;
    }
    *pUnk = p;
    InterlockedIncrement(&OutstandingObjects);
    return S_OK;
}
